namespace DanMuHelper.Audio
{
    using System.Collections.Generic;
    using UnityEngine;
    using DanMuHelper.Cache;
    using DanMuHelper.Tools;
    using UnityEngine.Audio;

    /// <summary>
    /// 音效组控制，主要用于高性能音效播放，限制音效播放数量
    /// </summary>
    public class CachedAudioGroup
    {
        /// <summary>
        /// 限制最大播放个数(-1: 不限制)
        /// </summary>
        public int maxPlay = -1;
        /// <summary>
        /// 防止爆音， 连续两次播放音效事件间隔太短，就跳过播放
        /// </summary>
        public float minPlayInterval = 0f;

        private AudioMixerGroup audioMixerGroup;

        private List<AudioObject> _objects;
        private ObjectPool<AudioObject> _cache;
        private float _lastPlayTime;

        public ObjectPool<AudioObject> Cache => _cache;

        public CachedAudioGroup(AudioMixerGroup audioMixerGroup, int maxPlay, string title = "AudioCtrl")
        {
            this.audioMixerGroup = audioMixerGroup;
            var audioObj = new GameObject(title);
            var source = audioObj.AddComponent<AudioSource>();
            var audioObjPrefab = audioObj.AddComponent<AudioObject>();
            audioObjPrefab.gameObject.SetActive(false);
            //TODO : 设置AudioSource 混声器

            source.outputAudioMixerGroup = audioMixerGroup;
            this.maxPlay = maxPlay;
            this._objects = new List<AudioObject>();
            this._cache = new(audioObjPrefab, maxPlay, false
                , () => Object.Instantiate(audioObjPrefab)
                , (obj, firstInit) => { obj.gameObject.SetActive(true); }
                , (obj) => Object.Destroy(obj.gameObject)
                , (obj) => obj.gameObject.SetActive(false)
            )
            { AutoDestructWait = 10.0f };
        }

        /// <summary>
        /// 单次播放后自动停止
        /// </summary>
        /// <param name="clip">音效clip</param>
        /// <param name="mute">是否静音</param>
        /// <returns></returns>
        public AudioObject PlayClip(AudioClip clip, bool mute, bool isLoop = false)
        {
            var now = Time.unscaledTime;
            if (minPlayInterval > 0f && (now - _lastPlayTime) <= minPlayInterval)
            {
                // 两次播放间隔时间太短，跳过
                return null;
            }
            if (maxPlay > 0 && _objects.Count >= maxPlay)
            {
                Trim();
            }
            _lastPlayTime = now;
            var go = _cache.GetOrCreate();
            go.PlayClip(clip, mute, isLoop);
            _objects.Add(go);
            return go;
        }

        /// <summary>
        /// 单次播放后自动停止
        /// </summary>
        /// <param name="clip">音效clip</param>
        /// <param name="mute">是否静音</param>
        /// <returns></returns>
        public AudioObject PlayOneShot(AudioClip clip, bool mute, float volume = 1f)
        {
            var now = Time.unscaledTime;
            if (minPlayInterval > 0f && (now - _lastPlayTime) <= minPlayInterval)
            {
                // 两次播放间隔时间太短，跳过
                return null;
            }
            if (maxPlay > 0 && _objects.Count >= maxPlay)
            {
                Trim();
            }
            _lastPlayTime = now;
            var go = _cache.GetOrCreate();
            go.PlayOneShot(clip, mute, volume);
            _objects.Add(go);
            return go;
        }

        /// <summary>
        /// 单次播放后自动停止
        /// </summary>
        /// <param name="clip">音效clip</param>
        /// <param name="mute">是否静音</param>
        /// <returns></returns>
        public AudioObject PlayOneShot(AudioClip clip, bool mute, Vector3 pos, float volume = 1)
        {
            var now = Time.unscaledTime;
            if (minPlayInterval > 0f && (now - _lastPlayTime) <= minPlayInterval)
            {
                // 两次播放间隔时间太短，跳过
                return null;
            }
            if (maxPlay > 0 && _objects.Count >= maxPlay)
            {
                Trim();
            }
            _lastPlayTime = now;
            var go = _cache.GetOrCreate();
            go.PlayOneShot(clip, mute, pos, volume);
            _objects.Add(go);
            return go;
        }

        /// <summary>
        /// 全体静音
        /// </summary>
        public void MuteAll()
        {
            var n = _objects.Count;
            for (int index = 0; index < n; index++)
            {
                _objects[index].Mute();
            }
        }

        /// <summary>
        /// 解除静音
        /// </summary>
        public void UnmuteAll()
        {
            var n = _objects.Count;
            for (int index = 0; index < n; index++)
            {
                _objects[index].Unmute();
            }
        }

        /// <summary>
        /// 修剪，如果音效太多，强制停止最早播放的
        /// </summary>
        private void Trim()
        {
            var n = _objects.Count;
            var minTime = float.MaxValue;
            var targetIndex = -1;
            for (int index = 0; index < n; index++)
            {
                if (_objects[index].playStartTime < minTime)
                {
                    targetIndex = index;
                    minTime = _objects[index].playStartTime;
                }
            }
            if (targetIndex >= 0)
            {
                _objects[targetIndex].Stop();
                Debug.LogError("AudioGroup Trim:" + _objects[targetIndex].GetInstanceID());
                _cache.Reclaim(_objects[targetIndex]);
                _objects.FastRemoveAt(targetIndex);
            }
        }



        /// <summary>
        /// 停止所有音效
        /// </summary>
        public void StopAll()
        {
            var n = _objects.Count;
            for (int index = 0; index < n; index++)
            {
                _objects[index].Stop();
            }
            _objects.Clear();
        }

        public void Update()
        {
            var count = _objects.Count;
            for (int index = 0; index < count;)
            {
                if (_objects[index].playStarted)
                {
                    var curr = _objects[index];
                    if (!curr.audioSource.isPlaying)
                    {
                        curr.Stop();
                        _objects.FastRemoveAt(index);
                        _cache.Reclaim(curr);
                        count--;
                        continue;
                    }
                }
                index++;
            }
            _cache.AutoDestruct();
        }
    }

}
